package protobuf.codec;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import protobuf.ProtoInstance;
import protobuf.protoClass.ProtoMsg;

import java.util.List;

/**
 * <pre>
 * （1）读取长度 {@link protobuf.codec.MyProtobufEncoder}，如果长度位数不够，就终止读取。
 * （2）读取魔数 magic、版本号 version等其他字段。
 * （3）按照净长度 length读取内容。如果内容 content的字节数不够，
 *      则恢复到之前的起始位置（也就是长度的位置），然后终止读取。
 *
 * </pre>
 */

public class MyProtobufDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf_in, List<Object> list_out) throws Exception {

        // 设置 读标记
        byteBuf_in.markReaderIndex();
        /*
         * @see protobuf.codec.MyProtobufEncoder
         * head 长度为 8 = 2 + 2 + 4
         * 长度小于 head直接 return
         */
        if (byteBuf_in.readableBytes() < 8) {
            return;
        }
        //读取 magic 并判断
        short magic = byteBuf_in.readShort();
        if (magic != ProtoInstance.MAGIC_CODE) {
            String error = "客户端口令错误：" + channelHandlerContext.channel().remoteAddress();
            throw new Exception(error);
        }
        // 读取 version 并判断
        short version = byteBuf_in.readShort();
        if (version != ProtoInstance.VERSION_CODE) {
            String error = "协议版本错误：" + channelHandlerContext.channel().remoteAddress();
            throw new Exception(error);
        }
        /**
         * +-------+---------+--------+-----------------------------+
         * | magic | version | length |   content (readableBytes)   |
         * +-------+---------+--------+-----------------------------+
         * <----------head------------><-----------content---------->
         *   读取 length 并判断
         *              < 0 ,直接关闭
         *              > 可读长度，即 content内容，说明 content丢失数据。
         *                         调用 resetReaderIndex()把 readerIndex重置到原来打标记的位置
         *                         即 markReaderIndex标记的位置。
         */
        int length = byteBuf_in.readInt();
        if (length < 0) {
            channelHandlerContext.close();
        }
        if (length > byteBuf_in.readableBytes()) {
            byteBuf_in.resetReaderIndex();
            return;
        }
        System.out.println("decoder length = " + byteBuf_in.readableBytes());
        // 将 content读取到 bytes数组中
        byte[] bytes;
        if (byteBuf_in.hasArray()) {
            // 堆缓冲，slice直接划分 ByteBuf空间,避免重复读
            bytes = byteBuf_in.slice(byteBuf_in.readerIndex(), length).array();
        } else {
            // 直接缓冲
            bytes = new byte[length];
            byteBuf_in.readBytes(bytes, 0, length);
        }
        // 将 bytes数组中的内容解析为 message对象,并加入到 list
        ProtoMsg.Message parseContent = ProtoMsg.Message.parseFrom(bytes);
        if (parseContent != null) {
            list_out.add(parseContent);
        }
    }
}
